{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "import tensorflow.keras.layers as layer\n",
    "from sklearn.preprocessing import LabelEncoder\n",
    "# from tensorflow.keras.models import Model\n",
    "from tensorflow.keras import Model\n",
    "import tensorflow.keras.backend as K\n",
    "from sklearn.model_selection import StratifiedKFold"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>label</th>\n",
       "      <th>I1</th>\n",
       "      <th>I2</th>\n",
       "      <th>I3</th>\n",
       "      <th>I4</th>\n",
       "      <th>I5</th>\n",
       "      <th>I6</th>\n",
       "      <th>I7</th>\n",
       "      <th>I8</th>\n",
       "      <th>I9</th>\n",
       "      <th>...</th>\n",
       "      <th>C17</th>\n",
       "      <th>C18</th>\n",
       "      <th>C19</th>\n",
       "      <th>C20</th>\n",
       "      <th>C21</th>\n",
       "      <th>C22</th>\n",
       "      <th>C23</th>\n",
       "      <th>C24</th>\n",
       "      <th>C25</th>\n",
       "      <th>C26</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1382.0</td>\n",
       "      <td>4.0</td>\n",
       "      <td>15.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>181.0</td>\n",
       "      <td>...</td>\n",
       "      <td>e5ba7672</td>\n",
       "      <td>f54016b9</td>\n",
       "      <td>21ddcdc9</td>\n",
       "      <td>b1252a9d</td>\n",
       "      <td>07b5194c</td>\n",
       "      <td>NaN</td>\n",
       "      <td>3a171ecb</td>\n",
       "      <td>c5c50484</td>\n",
       "      <td>e8b83407</td>\n",
       "      <td>9727dd16</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>0</td>\n",
       "      <td>44.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>102.0</td>\n",
       "      <td>8.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>4.0</td>\n",
       "      <td>...</td>\n",
       "      <td>07c540c4</td>\n",
       "      <td>b04e4670</td>\n",
       "      <td>21ddcdc9</td>\n",
       "      <td>5840adea</td>\n",
       "      <td>60f6221e</td>\n",
       "      <td>NaN</td>\n",
       "      <td>3a171ecb</td>\n",
       "      <td>43f13e8b</td>\n",
       "      <td>e8b83407</td>\n",
       "      <td>731c3655</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>14.0</td>\n",
       "      <td>767.0</td>\n",
       "      <td>89.0</td>\n",
       "      <td>4.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>245.0</td>\n",
       "      <td>...</td>\n",
       "      <td>8efede7f</td>\n",
       "      <td>3412118d</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>e587c466</td>\n",
       "      <td>ad3062eb</td>\n",
       "      <td>3a171ecb</td>\n",
       "      <td>3b183c5c</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0</td>\n",
       "      <td>NaN</td>\n",
       "      <td>893</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>4392.0</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>...</td>\n",
       "      <td>1e88c74f</td>\n",
       "      <td>74ef3502</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>6b3a5ca6</td>\n",
       "      <td>NaN</td>\n",
       "      <td>3a171ecb</td>\n",
       "      <td>9117a34a</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0</td>\n",
       "      <td>3.0</td>\n",
       "      <td>-1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>3.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>...</td>\n",
       "      <td>1e88c74f</td>\n",
       "      <td>26b3c7a7</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>21c9516a</td>\n",
       "      <td>NaN</td>\n",
       "      <td>32c7478e</td>\n",
       "      <td>b34f3128</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>5 rows × 40 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "   label   I1   I2    I3    I4      I5    I6    I7   I8     I9  ...       C17  \\\n",
       "0      0  1.0    1   5.0   0.0  1382.0   4.0  15.0  2.0  181.0  ...  e5ba7672   \n",
       "1      0  2.0    0  44.0   1.0   102.0   8.0   2.0  2.0    4.0  ...  07c540c4   \n",
       "2      0  2.0    0   1.0  14.0   767.0  89.0   4.0  2.0  245.0  ...  8efede7f   \n",
       "3      0  NaN  893   NaN   NaN  4392.0   NaN   0.0  0.0    0.0  ...  1e88c74f   \n",
       "4      0  3.0   -1   NaN   0.0     2.0   0.0   3.0  0.0    0.0  ...  1e88c74f   \n",
       "\n",
       "        C18       C19       C20       C21       C22       C23       C24  \\\n",
       "0  f54016b9  21ddcdc9  b1252a9d  07b5194c       NaN  3a171ecb  c5c50484   \n",
       "1  b04e4670  21ddcdc9  5840adea  60f6221e       NaN  3a171ecb  43f13e8b   \n",
       "2  3412118d       NaN       NaN  e587c466  ad3062eb  3a171ecb  3b183c5c   \n",
       "3  74ef3502       NaN       NaN  6b3a5ca6       NaN  3a171ecb  9117a34a   \n",
       "4  26b3c7a7       NaN       NaN  21c9516a       NaN  32c7478e  b34f3128   \n",
       "\n",
       "        C25       C26  \n",
       "0  e8b83407  9727dd16  \n",
       "1  e8b83407  731c3655  \n",
       "2       NaN       NaN  \n",
       "3       NaN       NaN  \n",
       "4       NaN       NaN  \n",
       "\n",
       "[5 rows x 40 columns]"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "train = pd.read_csv('./data/criteo_sampled_data.csv')\n",
    "train.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [],
   "source": [
    "# train.info()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [],
   "source": [
    "cols = train.columns[1:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [],
   "source": [
    "dense_feats = [f for f in cols if f[0] == 'I']\n",
    "sparse_feats = [f for f in cols if f[0] == 'C']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [],
   "source": [
    "def process_dense_feats(data, feats):\n",
    "    d = data.copy()\n",
    "    d = d[feats].fillna(0)\n",
    "    for f in feats:\n",
    "        d[f] = d[f].apply(lambda x: np.log(x+1) if x>-1 else -1)\n",
    "    return d\n",
    "data_dense = process_dense_feats(train, dense_feats)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [],
   "source": [
    "def process_sparse_feats(data, feats):\n",
    "    d = data.copy()\n",
    "    d = d[feats].fillna('-1')\n",
    "    for f in feats:\n",
    "        d[f] = LabelEncoder().fit_transform(d[f])\n",
    "    return d\n",
    "data_sparse = process_sparse_feats(train, sparse_feats)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "total_data = pd.concat([data_dense, data_sparse], axis=1)\n",
    "total_data['label'] = train['label']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 如果你只是想对流经该层的数据做个变换，而这个变换本身没有什么需要学习的参数，那么直接用Lambda Layer是最合适的了"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 获取类别型特征的大小\n",
    "sparse_feat_config= dict()\n",
    "for col in sparse_feats:\n",
    "    sparse_feat_config[col] = total_data[col].nunique()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 构造验证集和训练集\n",
    "train_data = total_data.loc[:500000-1]\n",
    "valid_data = total_data.loc[500000:]\n",
    "\n",
    "train_dense_x = [train_data[f].values for f in dense_feats]#  train_data[dense_feats] \n",
    "train_sparse_x = [train_data[f].values for f in sparse_feats] # train_data[sparse_feats] # \n",
    "train_label = train_data['label'].values\n",
    "train_label = tf.cast(train_label, tf.int32)\n",
    "\n",
    "val_dense_x = [valid_data[f].values for f in dense_feats] # valid_data[dense_feats]   \n",
    "val_sparse_x = [valid_data[f].values for f in sparse_feats] # valid_data[sparse_feats]\n",
    "val_label = valid_data['label'].values\n",
    "val_label = tf.cast(val_label, tf.int32)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 构造训练集和测试集\n",
    "def make_data(total_data,idx):\n",
    "    train_data = total_data.loc[idx,:]\n",
    "    train_dense_x = [train_data[f].values for f in dense_feats]\n",
    "    train_sparse_x = [train_data[f].values for f in sparse_feats]\n",
    "    train_label = train_data['label'].values\n",
    "    return train_sparse_x,train_dense_x,train_label"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 写法一\n",
    "继承layer,定义不同功能的层"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# 独立层：嵌入层\n",
    "class Embedding_dense(tf.keras.layers.Layer):\n",
    "    def __init__(self,sparse_feat_config, embeding_shape):\n",
    "        super(Embedding_dense, self).__init__()\n",
    "        # l2正则化\n",
    "        self.reg_1 = tf.keras.regularizers.l2(0.1)\n",
    "        self.embed_first = {}\n",
    "        self.sparse_feat_config = sparse_feat_config\n",
    "        self.embeding_shape = embeding_shape\n",
    "        self.sparse_feat = list(sparse_feat_config.keys())\n",
    "        for key, value in self.sparse_feat_config.items():\n",
    "            self.embed_first[key] = layer.Embedding(value+1,self.embeding_shape, \n",
    "                                                    embeddings_regularizer=self.reg_1, \n",
    "                                                    name='embed'+key)\n",
    "    def call(self,x_sparse):\n",
    "        embed_lookup_first = []\n",
    "        for i,key in enumerate(self.sparse_feat):\n",
    "\n",
    "            _embed = self.embed_first[key](x_sparse[i])\n",
    "\n",
    "            embed_lookup_first.append(_embed)\n",
    "\n",
    "        return embed_lookup_first\n",
    "    \n",
    "    def get_config(self):\n",
    "\n",
    "        config = super().get_config().copy()\n",
    "        config.update({\n",
    "            'sparse_feat_config': self.sparse_feat_config,\n",
    "            'embeding_shape': self.embeding_shape,\n",
    "        })\n",
    "        return config\n",
    "# t = Embedding_dense(sparse_feat_config,1)\n",
    "# y = t(inputs_sparse)    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 独立层：DNN\n",
    "class DNN(tf.keras.layers.Layer):\n",
    "    def __init__(self,dnn_config=[128,128,64]):\n",
    "        super(DNN, self).__init__()\n",
    "        self.dnn_layers_config = dnn_config\n",
    "        self.dnn_layers = []\n",
    "        for s in self.dnn_layers_config:\n",
    "            self.dnn_layers.append(layer.Dense(s,activation='relu'))\n",
    "            \n",
    "    def call(self,embed_lookup_second):\n",
    "#         embed_lookup_second = layer.Concatenate(axis=1)(embed_lookup_second) \n",
    "        fc_layer = layer.Flatten()(embed_lookup_second)\n",
    "        for i,_ in enumerate(self.dnn_layers):\n",
    "            fc_layer = self.dnn_layers[i](fc_layer)\n",
    "        \n",
    "        return fc_layer\n",
    "    \n",
    "    def get_config(self):\n",
    "        config = super().get_config().copy()\n",
    "        config.update({\n",
    "            'dnn_layers_config': self.dnn_layers_config,\n",
    "        })\n",
    "        return config"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 交叉层\n",
    "class cin_cross_layer(tf.keras.layers.Layer):\n",
    "    def __init__(self,x_0,n_filters):\n",
    "        super(cin_cross_layer,self).__init__()\n",
    "        self.x_0 = x_0\n",
    "        self.n_filters = n_filters\n",
    "        self.x_0_embed_shape = x_0.shape[-1]\n",
    "\n",
    "    def call(self, x_l):\n",
    "        # x_0 *x_l^T\n",
    "        k = self.x_0_embed_shape\n",
    "        # 1.将x0与xl按照k所在的维度（-1）进行拆分，每个都可以拆成k列\n",
    "        # 与论文给的图相反，为了便于计算，横着切的\n",
    "        x0_cols = tf.split(self.x_0,k,axis=-1)\n",
    "        xl_cols = tf.split(x_l,k,axis=-1)\n",
    "        \n",
    "        # 2.遍历k列，对于x0与xl所在的第i列进行外积计算，存在feature_maps中\n",
    "        feature_maps = []\n",
    "        for i in range(k):\n",
    "            feature_map = tf.matmul(xl_cols[i], x0_cols[i], transpose_b=True)\n",
    "            feature_map = tf.expand_dims(feature_map,axis=-1)\n",
    "            feature_maps.append(feature_map)\n",
    "#             break\n",
    "        feature_maps  = layer.Concatenate()(feature_maps)  # ?,h,m,k\n",
    "    \n",
    "        # 3.压缩网络\n",
    "        x0_n_feats = self.x_0.get_shape()[1]  # m\n",
    "        xl_n_feats = x_l.get_shape()[1]  # h\n",
    "        reshaped_feature_maps = layer.Reshape(target_shape=(k,x0_n_feats*xl_n_feats))(feature_maps)\n",
    "\n",
    "        # 4.Conv1D：使用 n_filters 个形状为 1 * (h*m) 的卷积核以 1 为步长，\n",
    "        # 按嵌入维度 D 的方向进行卷积，最终得到形状为 ？, D, n_filters 的输出\n",
    "        new_feature_maps  = layer.Conv1D(self.n_filters,1,1)(reshaped_feature_maps)\n",
    "        new_feature_maps = tf.transpose(new_feature_maps,[0,2,1])\n",
    "        return new_feature_maps\n",
    "    def get_config(self):\n",
    "        config = super().get_config().copy()\n",
    "        config.update({\n",
    "            'x_0': self.x_0,\n",
    "            'n_filters':self.n_filters,\n",
    "        })\n",
    "        return config  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [],
   "source": [
    "class xDeepFM:\n",
    "    def __init__(self,sparse_feat_config,dense_feats):\n",
    "        \n",
    "        self.sparse_feat_config= sparse_feat_config\n",
    "        self.inputs_sparse, self.inputs_dense = self.build_input(sparse_feat_config,dense_feats)\n",
    "\n",
    "        self.Embedding_8_dense = Embedding_dense(self.sparse_feat_config,8)\n",
    "        self.Embedding_1_dense = Embedding_dense(self.sparse_feat_config,1)\n",
    "\n",
    "        self.DNN = DNN([128,128,64])\n",
    "        \n",
    "        self.xDeepFM =  self.build_model() \n",
    "    def build_input(self,sparse_feat_config,dense_feats):\n",
    "        inputs_sparse = []\n",
    "        inputs_dense = []\n",
    "        for key in sparse_feat_config:\n",
    "            inputs_sparse.append(layer.Input(shape=(1,),name=key))\n",
    "        for key in dense_feats:\n",
    "            inputs_dense.append(layer.Input(shape=(1,),name=key))\n",
    "        \n",
    "        return inputs_sparse, inputs_dense\n",
    "\n",
    "    def build_model(self, num_lays = 3):\n",
    "        \n",
    "        # ------------------Linear层--------------------\n",
    "        # sparse特征嵌入\n",
    "        sparse_embed_1_lookup =  self.Embedding_1_dense(self.inputs_sparse)\n",
    "        concat_sparse_inputs = layer.Concatenate(axis=1)(sparse_embed_1_lookup)\n",
    "        concat_sparse_inputs = layer.Flatten()(concat_sparse_inputs)\n",
    "        fst_order_sparse_layer   = layer.Dense(1)(concat_sparse_inputs)\n",
    "        # dense 特征\n",
    "        concat_dense_inputs = layer.Concatenate(axis=1)(self.inputs_dense)\n",
    "        fst_order_dense_layer = layer.Dense(1)(concat_dense_inputs)\n",
    "        # dense sparse 二者的结果再求和\n",
    "        linear_part = layer.Add()([fst_order_sparse_layer,fst_order_dense_layer])\n",
    "#         print(linear_part)\n",
    "        \n",
    "        # sparse 嵌入，作为CIN和plain DNN 的输入\n",
    "        sparse_embed_lookup =  self.Embedding_8_dense(self.inputs_sparse)\n",
    "        concat_sparse_inputs = layer.Concatenate(axis=1)(sparse_embed_lookup)\n",
    "        \n",
    "        #----------------CIN层---------------------------\n",
    "        # 初始化层,\n",
    "        n_cin = [12,9,15]\n",
    "        cin_cross_layers = []\n",
    "        for i in range(3):\n",
    "            cin_cross_layers.append(cin_cross_layer(concat_sparse_inputs,n_cin[i])) \n",
    "        # 调用层，并开始计算CIN\n",
    "        cin_layer = []\n",
    "        x_l = concat_sparse_inputs\n",
    "        for i in range(3):\n",
    "            x_l  = cin_cross_layers[i](x_l)\n",
    "            cin_layer.append(x_l)\n",
    "        # concat 并求和    \n",
    "        cin_layer = layer.Concatenate(axis=1)(cin_layer)\n",
    "        cin_layer_sum = layer.Lambda(lambda x: K.sum(x,axis=-1))(cin_layer)\n",
    "#         print( cin_layer_sum )\n",
    "        #----------------DNN层--------------------------- \n",
    "        fc_part = self.DNN(concat_sparse_inputs)\n",
    "#         print(fc_part)\n",
    "        \n",
    "        #----------------组合CIN和DNN和Linear---------------------\n",
    "        concat_layer = layer.Concatenate(axis=1)([linear_part,cin_layer_sum,fc_part])\n",
    "        output_layer = layer.Dense(1,activation='sigmoid')(concat_layer)\n",
    "#         print(output_layer)\n",
    "#         初始化模型\n",
    "        model = Model(self.inputs_sparse + self.inputs_dense, outputs=output_layer)\n",
    "        model.compile(optimizer = tf.keras.optimizers.RMSprop(learning_rate=1e-3),\n",
    "                      loss= 'binary_crossentropy',\n",
    "                      metrics=['AUC'])\n",
    "        return model\n",
    "    \n",
    "\n",
    "    def train(self,train_data,train_label,valid_data, valid_label,batch_size,epochs,callbacks):\n",
    "        self.xDeepFM.fit(train_data,train_label,\n",
    "                  batch_size=batch_size, epochs=epochs, verbose=1, \n",
    "                  validation_data=(valid_data, valid_label),\n",
    "                  callbacks = callbacks\n",
    "                 )\n",
    "# xDeepFM(sparse_feat_config, dense_feats).build_model()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fold: 0\n",
      "Train on 480000 samples, validate on 120000 samples\n",
      "480000/480000 [==============================] - ETA: 0s - loss: 165.1489 - AUC: 0.4751\n",
      "Epoch 00001: val_loss improved from inf to 12.32200, saving model to ./model/0.h5\n",
      "480000/480000 [==============================] - 18s 38us/sample - loss: 165.1489 - AUC: 0.4751 - val_loss: 12.3220 - val_AUC: 0.5774 - lr: 0.0010\n",
      "fold: 1\n",
      "Train on 480000 samples, validate on 120000 samples\n",
      "480000/480000 [==============================] - ETA: 0s - loss: 165.2818 - AUC: 0.5615\n",
      "Epoch 00001: val_loss improved from inf to 12.31788, saving model to ./model/1.h5\n",
      "480000/480000 [==============================] - 18s 38us/sample - loss: 165.2818 - AUC: 0.5615 - val_loss: 12.3179 - val_AUC: 0.6594 - lr: 0.0010\n",
      "fold: 2\n",
      "Train on 480000 samples, validate on 120000 samples\n",
      "480000/480000 [==============================] - ETA: 0s - loss: 165.0523 - AUC: 0.5822\n",
      "Epoch 00001: val_loss improved from inf to 12.28506, saving model to ./model/2.h5\n",
      "480000/480000 [==============================] - 18s 38us/sample - loss: 165.0523 - AUC: 0.5822 - val_loss: 12.2851 - val_AUC: 0.6826 - lr: 0.0010\n",
      "fold: 3\n",
      "Train on 480000 samples, validate on 120000 samples\n",
      "480000/480000 [==============================] - ETA: 0s - loss: 165.2361 - AUC: 0.6585\n",
      "Epoch 00001: val_loss improved from inf to 12.29144, saving model to ./model/3.h5\n",
      "480000/480000 [==============================] - 18s 38us/sample - loss: 165.2361 - AUC: 0.6585 - val_loss: 12.2914 - val_AUC: 0.7084 - lr: 0.0010\n",
      "fold: 4\n",
      "Train on 480000 samples, validate on 120000 samples\n",
      "480000/480000 [==============================] - ETA: 0s - loss: 165.1713 - AUC: 0.5798\n",
      "Epoch 00001: val_loss improved from inf to 12.29164, saving model to ./model/4.h5\n",
      "480000/480000 [==============================] - 18s 37us/sample - loss: 165.1713 - AUC: 0.5798 - val_loss: 12.2916 - val_AUC: 0.6850 - lr: 0.0010\n"
     ]
    }
   ],
   "source": [
    "# 五折交叉 + 提前停止 + 保存模型\n",
    "tf.compat.v1.disable_eager_execution()\n",
    "\n",
    "skf = StratifiedKFold(n_splits = 5, random_state=1996, shuffle = True)\n",
    "for idx, (train_idx, val_idx) in enumerate(skf.split(total_data,total_data['label'])):\n",
    "    print('fold:',idx)\n",
    "    K.clear_session()\n",
    "    train_sparse_x,train_dense_x,train_label = make_data(total_data,train_idx)\n",
    "    val_sparse_x,val_dense_x,val_label = make_data(total_data,val_idx) \n",
    "    # 定义回调\n",
    "    \n",
    "    # 保存模型\n",
    "    file_path = f'./model/{idx}.h5'\n",
    "\n",
    "    checkpoint = tf.keras.callbacks.ModelCheckpoint(file_path, monitor='val_loss', verbose=1, save_best_only=True,save_weights_only=True, mode='min')\n",
    "    # metric 不提高时，减小学习率\n",
    "    reduce_lr = tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.8, patience=1, min_lr=0.0001, verbose=1)\n",
    "    # val_loss 连续两次提升小于 1e-2，提前停止\n",
    "    earlystopping = tf.keras.callbacks.EarlyStopping(monitor='val_loss', min_delta=0.0001, patience=2,verbose=1, mode='auto')\n",
    "    callbacks = [checkpoint, reduce_lr, earlystopping]\n",
    "\n",
    "    # 初始化模型\n",
    "    xDeepFM_ctr = xDeepFM(sparse_feat_config, dense_feats)\n",
    "    xDeepFM_ctr.train(train_sparse_x+train_dense_x,train_label,\n",
    "                 val_sparse_x+val_dense_x,val_label,\n",
    "                12800,1, callbacks=callbacks)    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.21080574],\n",
       "       [0.20318323],\n",
       "       [0.10701928],\n",
       "       ...,\n",
       "       [0.21335861],\n",
       "       [0.14328235],\n",
       "       [0.09570071]], dtype=float32)"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 模型的加载与预测\n",
    "xdeepfm = xDeepFM(sparse_feat_config, dense_feats).build_model()\n",
    "xdeepfm.load_weights('./model/0.h5')\n",
    "xdeepfm.predict(val_sparse_x+val_dense_x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
